/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

 import java.util.Arrays;
 import java.security.MessageDigest;
 
 
/**
 *
 * @author Edvin
 */
public class GameState {
	static final byte CELL_PLAYER=0;			///< the cell is the player
	static final byte CELL_BLOCK=1;				///< the cell is a block
//	static final byte CELL_ON_GOAL=(1<<1);		///< the cell is on a goal
	static final byte CELL_INVALID=(1<<6);		///< the cell is invalid

	static boolean isPlayer(byte b){ return (b^CELL_PLAYER)==0 ? true : false; }
	static boolean isBlock(byte b){ return (b&CELL_BLOCK)!=0 ? true : false; }
//	static boolean isOnGoal(byte b){ return (b&CELL_ON_GOAL)!=0 ? true : false; }
	static boolean isInvalid(byte b){ return (b&CELL_INVALID)!=0 ? true : false; }

	byte[] gs;
	int[][] pos;
	Board board;

	int indexPlayer=-1;

	public GameState() {
	}

	public GameState(String flatBoard, Board board){
		initGameState(flatBoard, board);
	}

	// clone a gs
	public GameState(GameState gs){
//		this.gs = gs.gs.clone();
		this.board = gs.board;
		this.gs = gs.gs;
//		this.pos = gs.pos.clone();
		this.pos = U.matrixClone(gs.pos);
		this.indexPlayer = gs.indexPlayer;
	}

	public void initGameState(String flatBoard, Board board){
		this.board = board;
		int count = 0;
		for(char c : flatBoard.toCharArray()){
			if(c == '@' || c == '$' || c == '*' || c == '+')
				count++;
		}
		gs = new byte[count];
		pos = new int[count][2];
	}

// !!! doMove modifierar denna klass !!!
	GameState doMove(Board board, Move move){
//		pr("playerPos: "+getPlayerPos()[0]+" "+getPlayerPos()[1]);
//		U.whatAmI_Move(move);
		// position where the player will be
		int[] p2 = board.posInDirection(move, getPlayerPos());
		if(p2[0]<0 || p2[1]<0){
			pr("Error1(doMove(Board board, Move move)), the position is invalid! r:"+p2[0]+"	c:"+p2[1]);
			return null;
		}
		if(board.isInvalid(p2)){
			pr("Error2(doMove(Board board, Move move)), the position is invalid! r:"+p2[0]+"	c:"+p2[1]);
			return null;
		}if(board.isWall(p2)){
			pr("Error2(doMove(Board board, Move move)), the position is wall! r:"+p2[0]+"	c:"+p2[1]);
			return null;
		}

		if(isBlock(At(p2))){// have to push
//			U.whatAmI_Move(move);
			int[] b2 = board.posInDirection(move, p2);
			if(b2[0]<0 || b2[1]<0){
				pr("Error3(doMove(Board board, Move move)), the position is invalid! r:"+b2[0]+"	c:"+b2[1]);
				return null;
			}
			if(board.isInvalid(b2) || board.isWall(b2)){
				pr("Error4(doMove(Board board, Move move)), the position is invalid or wall! r:"+b2[0]+"	c:"+b2[1]);
				return null;
			}
			changePos(p2, b2);
			setPlayer(indexPlayer, p2);
		}else{
			setPlayer(indexPlayer, p2);
		}
		return this;
	}

	

	boolean isSolved(Board board){
		for(int i = 0; i < board.mCell.length; i++){
			int[] rc = board.cellToRowCol(i);
			if(isBlock(At(rc)) && !isBlockAtGoal(board, this, rc)){
				return false;
			}
		}
		return true;
	}

	static boolean isBlockAtGoal(Board board, GameState gs, int[] rc){
		if(rc[0]>=0 && rc[1]>=0 && board.isGoal(rc[0], rc[1])){
			if(isBlock(gs.At(rc))){
				return true;
			}
		}
		return false;
	}
	static boolean isPlayerAtGoal(Board board, GameState gs, int[] rc){
		if(rc[0]>=0 && rc[1]>=0 && board.isGoal(rc[0], rc[1])){
			if(isPlayer(gs.At(rc))){
				return true;
			}
		}
		return false;
	}

	void changePos(int[] rc, int[] rc2){
		int index = rowColToCell(rc);
		if(index >= 0){
			pos[index]=rc2;
		}else{
			pr("Error(changePos(int[] rc, int[] rc2)), the position is invalid! r:"+rc[0]+"	c:"+rc[1]);
		}
	}

	void setPlayer(int index, int r, int c){
		indexPlayer = index;
		gs[index] = CELL_PLAYER;
		pos[index] = new int[]{r,c};
//		pr(pos[index], "setPlayer()");
	}
	void setBlock(int index, int r, int c){
		gs[index] = CELL_BLOCK;
		pos[index] = new int[]{r,c};
//		pr(pos[index], "setBlock()");
	}
	void setPlayer(int index, int[] rc){
		indexPlayer = index;
		gs[index] = CELL_PLAYER;
		pos[index] = rc;
//		pr(pos[index], "setPlayer()");
	}
	void setBlock(int index, int[] rc){
		gs[index] = CELL_BLOCK;
		pos[index] = rc;
//		pr(pos[index], "setBlock()");
	}


	int rowColToCell(int[] rc){
		for(int i = 0; i < pos.length; i++){
			if(pos[i][0] == rc[0] && pos[i][1] == rc[1]){
				return i;
			}
		}
		return -1;// fail
	}

	int rowColToCell(int r, int c){
		for(int i = 0; i < pos.length; i++){
			if(pos[i][0] == r && pos[i][1] == c){
				return i;
			}
		}
		return -1;// fail
	}

	int[] getPlayerPos(){
		if(indexPlayer>=0 && indexPlayer<pos.length)
			return pos[indexPlayer];
		pr("There is no player");
		System.exit(0);
		return new int[]{-1,-1};
	}
	int[] getBlockPos(int index){
		if(index>=0 && index<pos.length)
			return pos[index];
		return new int[]{-1,-1};// fail
	}
	int[] getBlockPos(int[] rc){
		return getBlockPos(rowColToCell(rc));
	}

	int[][] getBlockPoses(){
		int[][] blocks = new int[pos.length-1][];
		int blockIndex=0;
		for(int i = 0; i < pos.length; i++){
			if(i!=indexPlayer){
				blocks[blockIndex]=pos[i].clone();
				blockIndex++;
			}
		}
		return blocks;
	}

	byte getBlock(int index){
		if(index>=0 && index<pos.length)
			return gs[index];
		return CELL_INVALID;
	}
	byte getBlock(int[] rc){
		return getBlock(rowColToCell(rc));
	}

	byte At(int[] rc){
		int index = rowColToCell(rc);
		if(index>=0)
			return gs[index];
		return CELL_INVALID;
	}
	byte At(int r, int c){
		int index = rowColToCell(r, c);
		if(index>=0)
			return gs[index];
		return CELL_INVALID;
	}

	int[] posAt(int[] rc){
		int index = rowColToCell(rc);
		if(index>=0)
			return pos[index];
		return new int[]{-1,-1};// fail
	}

	
//	public GameState(Board board) {
//		// count dynamic elements in the board
//		int count = 0;
//		for(byte b : board.mCell)
//			if(Board.isPlayer(b)||Board.isBlock(b))
//				count++;
//		gs = new byte[count];
//
//		count = 0;
//		for(byte b : board.mCell){
//			if(Board.isPlayer(b)||Board.isBlock(b)){
//				gs[count] = b;
//				count++;
//			}
//		}
//	}

	int blocksOnGoal(Board b){
		int count = 0;
		for(int i = 0; i < gs.length; i++){
			if(isBlock(gs[i]) && isBlockAtGoal(b, this, getBlockPos(i))){
				count++;
			}
		}
		return count;
	}
	

	void pr(Object msg){
		U.pr(msg);
	}

	void pr(int[] pos, String s){
		for(int i = 0; i < pos.length; i++){
			System.out.print(pos[i]);
			System.out.print(" ");
		}
		System.out.println(s);
	}
    
    @Override
    public boolean equals(Object o)
	{
		if(this == o) {
			return true;
        }
		if((o == null) || (o.getClass() != this.getClass()))
        {
	    	return false;
        }
		GameState gs = (GameState)o;

        if (gs.pos.length != this.pos.length) {
            return false;
        }

        for (int i = 0; i < gs.pos.length; i++) {
            if (i == gs.indexPlayer) {
                continue;
            }

            boolean found = false;
            for (int j = 0; j < gs.pos.length; j++) {
                if (j == this.indexPlayer) {
                    continue;
                }

                if (Arrays.equals(gs.pos[i], this.pos[j])) {
                    found = true;
                    break;
                }
            }

            if (!found) {
                return false;
            }
        }
        // Stone configuration are equal after this point

        // Player pos
//        if (Arrays.equals(this.pos[this.indexPlayer], gs.pos[gs.indexPlayer])) {
            return true;
//        } else {
//            return U.pathFromTo(this.board, this, this.pos[this.indexPlayer], gs.pos[gs.indexPlayer]);
////            return false;
//        }
	}
    
    @Override 
    public int hashCode() {
        int x_sum = 0, y_sum = 0;
    
        for (int i = 0; i < this.pos.length; i++) {
//            if (i != this.indexPlayer) {
                x_sum += this.pos[i][0];
                y_sum += this.pos[i][1];
//            }
        }

        return x_sum * 1024 + y_sum;
    }
}
